package view.commonview;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JTabbedPane;

import model.editormodel.EditorModel;
import model.simulationmodel.SimulationModel;
import view.UtilityConstants;
import view.editorview.EditorArea;
import view.editorview.EditorPanel;
import view.editorview.EditorToolPanel;
import view.simulationview.SimulationPanel;
import view.simulationview.SimulationToolPanel;
import controller.commoncontroller.TabListener;
import controller.commoncontroller.UtilityDialogs;

/**
 * <b>Erbt:</b> <code>JTabbedPane</code> <br>
 * <b>In Verbindung stehende Klassen:</b> <code>EditorPanel, SimulationPanel
 * </code><br>
 * <b>Funktion:</b> Ermoeglicht Wechsel der Modi <br>
 * <br>
 * Registerpanel mit dem man zwischen Bearbeitungs- und Simulationsmodus
 * wechseln kann. Auf dieser Klasse liegen
 * <code>EditorPanel, SimulationPanel</code>.
 * 
 * @author Team Locsi
 * 
 */
@SuppressWarnings("serial")
public class TabbedPane extends JTabbedPane {

	/*
	 * Referenzen auf andere View-Objekte
	 */
	private EditorPanel editorPanel;
	private SimulationPanel simPanel;
	private InfoPanel iPanel;
	private ToolBar tools;

	/*
	 * Variablen fuer die ZoomFunktion
	 */
	private double zoomFactor = 1.0;
	public static final double ZOOMSTEP = 0.1;
	public static final double MAXZOOM = 1.9;
	public static final double MINZOOM = 0.3;

	/**
	 * Dieser Konstruktor erzeugt ein neues <code>TabbedPane</code>-Objekt,
	 * wobei auch die beiden Panel <code>EditorPanel</code> und
	 * <code>SimulationPanel</code> erzeugt werden.
	 * 
	 * @param eModel
	 *            <code>EditorModel</code> - Referenz auf das
	 *            <code>EditorModel</code>
	 * 
	 * @param sModel
	 *            <code>SimulationModel</code> - Referenz auf das
	 *            <code>SimulationModel</code>
	 * 
	 * @param cta
	 *            <code>CircuitTypeArea</code> - Referenz auf die
	 *            <code>CircuitTypeArea</code>
	 * 
	 * @param tools
	 *            <code>ToolBar</code> - Referenz auf die <code>ToolBar</code>
	 * @param infoPanel
	 *            <code>InfoPanel</code> - Referenz auf das
	 *            <code>InfoPanel</code>
	 */
	public TabbedPane(EditorModel eModel, SimulationModel sModel,
			CircuitTypeArea cta, ToolBar tools, InfoPanel infoPanel) {
		this.tools = tools;
		this.iPanel = infoPanel;
		this.editorPanel = new EditorPanel(eModel, this, cta, tools, infoPanel);
		this.simPanel = new SimulationPanel(sModel, this, tools, infoPanel);
		this.zoomFactor = 1.0F;
		this.addTab("Bearbeitungsmodus", new ImageIcon(getClass().getClassLoader().getResource(
				UtilityConstants.ICON_PATH + "schr2.png")), editorPanel, null);
		this.addTab("Simulationsmodus", new ImageIcon(getClass().getClassLoader().getResource(
				UtilityConstants.ICON_PATH + "run.png")), simPanel, null);

	}

	/**
	 * Diese Methode exportiert die aktuelle Schaltung als Bild, im uebergebenen
	 * Format.
	 * 
	 * @param path
	 *            <code>String</code> - Pfad an dem das Bild gespeichert werden
	 *            soll
	 */
	public void exportAsPicture(String path) {
		BufferedImage img = null;

		// je nach ausgewaehlten tab wird ein entsprechendes schaltungsbild
		// erzeugt
		if (this.getSelectedIndex() == 0) {
			img = new BufferedImage(
					(int) (getEditorArea().getWidth() * zoomFactor),
					(int) (getEditorArea().getHeight() * zoomFactor),
					BufferedImage.TYPE_INT_RGB);
			img = getEditorArea().paintToImage(img);
		} else {
			img = new BufferedImage(
					(int) (simPanel.getSimArea().getWidth() * zoomFactor),
					(int) (simPanel.getSimArea().getHeight() * zoomFactor),
					BufferedImage.TYPE_INT_RGB);
			img = simPanel.getSimArea().paintToImage(img);
		}

		// festlegen der groesse des schaltungsbildes
		int x = (int) (editorPanel.getEditorArea().getMinXValue() * zoomFactor);
		int y = (int) (editorPanel.getEditorArea().getMinYValue() * zoomFactor);
		int w = (int) ((editorPanel.getEditorArea().getMaxXValue() * zoomFactor) - x);
		int h = (int) ((editorPanel.getEditorArea().getMaxYValue() * zoomFactor) - y);
		img = img.getSubimage(Math.max(0, x - 12), Math.max(0, y - 15),
				Math.min(img.getWidth(), w + 40),
				Math.min(img.getHeight(), h + 40));
		BufferedImage bimg = null;

		// versucht das logo-bild einzulesen, dass auf jeden screenshot geaddet
		// wird
		try {
			bimg = ImageIO.read(getClass().getClassLoader().getResource(UtilityConstants.ICON_PATH
					+ "logoStamp1.png"));
		} catch (IOException e) {
			// wird das logo nicht gefunden, wird es nicht angezeigt
		}

		// erstellen des images, das letztendlich gespeichert wird
		BufferedImage finalImage = new BufferedImage(img.getWidth() + 60,
				img.getHeight() + 60, BufferedImage.TYPE_INT_RGB);
		Graphics2D g2d = finalImage.createGraphics();
		g2d.setColor(Color.WHITE);
		g2d.fillRect(0, 0, finalImage.getWidth(), finalImage.getHeight());
		g2d.drawImage(bimg, null, finalImage.getWidth() - bimg.getWidth(),
				finalImage.getHeight() - bimg.getHeight());
		g2d.drawImage(img, null, 20, 20);
		g2d.dispose();

		// versucht das erstellte bild abzuspeichern
		try {
			File file = new File(path);
			if (path.endsWith(UtilityDialogs.PNG_EXTENSION)) {
				ImageIO.write(finalImage, "png", file);
			} else if (path.endsWith(UtilityDialogs.JPG_EXTENSION)) {
				ImageIO.write(finalImage, "jpg", file);
			} else if (path.endsWith(UtilityDialogs.GIF_EXTENSION)) {
				ImageIO.write(finalImage, "gif", file);
			} else if (path.endsWith(UtilityDialogs.BMP_EXTENSION)) {
				ImageIO.write(finalImage, "bmp", file);
			}
			this.editorPanel.getEditorArea().getiArea().addInfoText("Schaltung erfolgreich als  '" + file.getName() + "' exportiert");
		} catch (IOException e1) {
			this.editorPanel
					.getEditorArea()
					.getiArea()
					.addInfoText(
							"Beim Abspeichern des Bildes ist ein Fehler aufgetreten!");
		}
	}

	/**
	 * Diese Methode gibt das <code>EditorArea</code>-Objekt zurueck.
	 * 
	 * @return <code>EditorArea</code> - das <code>EditorArea</code>-Objekt
	 */
	public EditorArea getEditorArea() {
		return editorPanel.getEditorArea();
	}

	/**
	 * Diese Methode gibt das <code>EditorToolPanel</code>-Objekt zurueck.
	 * 
	 * @return <code>EditorToolPanel</code> - das <code>EditorToolPanel</code>
	 *         -Objekt
	 */
	public EditorToolPanel getEditorToolPanel() {
		return editorPanel.getEditorToolPanel();
	}
	
	/**
	 * Diese Methode gibt das <code>SimulationToolPanel</code>-Objekt zurueck.
	 * 
	 * @return <code>SimulationToolPanel</code> - das <code>SimulationToolPanel</code>
	 *         -Objekt
	 */
	public SimulationToolPanel getSimToolPanel() {
		return simPanel.getSimToolPanel();
	}

	/**
	 * Setzt auf sich selbst einen ChangeListener. Wird in einer extra Methode
	 * behandelt, da sonst die Referenz der MenuBar noch nicht verfuegbar waere
	 * 
	 * @param eModel
	 *            <code>EditorModel</code> - Referenz auf das
	 *            <code>EditorModel</code>
	 * @param sModel
	 *            <code>SimulationModel</code> - Referenz auf das
	 *            <code>SimulationModel</code>
	 * @param mBar
	 *            <code>MenuBar</code> - Referenz auf die <code>MenuBar</code>
	 */
	protected void setMenuBar(EditorModel eModel, SimulationModel sModel,
			MenuBar mBar) {
		this.simPanel.getSimToolPanel().setTruthTableButtonInMenu(mBar.getTruthTableMenuItem());
		this.addChangeListener(new TabListener(eModel, sModel, this, mBar, tools));
	}
	
	/**
	 * Resettet den Zoomfaktor
	 */
	public void resetZoomFactor() {
		this.zoomFactor = 1.0;
		editorPanel.getEditorArea().checkChangeSizeNecessary();
		simPanel.getSimArea().setPreferredSize(getEditorArea().getPreferredSize());
		getEditorArea().paintRasterToImage();
		this.informView();
		this.tools.setZoomFactorLabel(zoomFactor);
	}

	/**
	 * Erhoeht den Zoomfaktor um den Faktor <code>ZOOMSTEP</code>. Dabei darf
	 * <code>MAXZOOM</code> nicht uberschritten werden.
	 */
	public void increaseZoomFactor() {
		this.zoomFactor = this.zoomFactor + TabbedPane.ZOOMSTEP;
		this.zoomFactor = Math.round(zoomFactor * 10) / 10.0;
		this.zoomFactor = Math.min(this.zoomFactor, MAXZOOM);
		editorPanel.getEditorArea().checkChangeSizeNecessary();
		simPanel.getSimArea().setPreferredSize(getEditorArea().getPreferredSize());
		getEditorArea().paintRasterToImage();
		this.informView();
		this.tools.setZoomFactorLabel(zoomFactor);
	}

	/**
	 * Verringert den Zoomfaktor um den Faktor <code>ZOOMSTEP</code>. Dabei darf
	 * <code>MINZOOM</code> nicht unterschritten werden.
	 */
	public void decreaseZoomFactor() {
		this.zoomFactor = this.zoomFactor - TabbedPane.ZOOMSTEP;
		this.zoomFactor = Math.round(zoomFactor * 10) / 10.0;
		this.zoomFactor = Math.max(this.zoomFactor, MINZOOM);
		editorPanel.getEditorArea().checkChangeSizeNecessary();
		simPanel.getSimArea().setPreferredSize(getEditorArea().getPreferredSize());
		getEditorArea().paintRasterToImage();
		this.informView();
		this.tools.setZoomFactorLabel(zoomFactor);
	}

	/**
	 * Gibt den aktuellen Zoomfaktor zurueck.
	 * 
	 * @return <code>double</code> - den aktuellen Zoomfaktor
	 */
	public double getZoomFactor() {
		return this.zoomFactor;
	}

	/**
	 * Benachrichtigt das <code>EditorPanel</code> bzw. das
	 * <code>SimulationPanel</code> sich neu zu zeichnen.
	 */
	private void informView() {
		if (this.getSelectedIndex() == 0) {
			this.editorPanel.getEditorArea().repaint();
			this.editorPanel.getEditorArea().revalidate();
		} else {
			// beim neuzeichnen wird der Zoomfaktor abgefragt
			this.simPanel.getSimArea().repaint();
			this.simPanel.getSimArea().revalidate();
		}
	}

	/**
	 * Gibt das <code>InfoPanel</code> zurueck
	 * 
	 * @return
	 */
	public InfoPanel getIPanel() {
		return iPanel;
	}
}